package edu.stanford.prpl.impl.directory;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.stanford.prpl.api.Directory;
import edu.stanford.prpl.api.PersistentIndex;
import edu.stanford.prpl.common.PRPLConstants;
import edu.stanford.prpl.common.PRPLProperties;

public class DirectoryServer implements Directory<Map<String, String>> {

	private static Log log = LogFactory.getLog(DirectoryServer.class);

	protected static final String LAST_UPDATED = "LAST_UPDATED";

	protected static PersistentIndex<String, Map<String, String>> index;
	protected static Thread workerThread;

	public DirectoryServer() {
	}

	//@Override
	public Map<String, String> getEntry(String key) {
		log.info("Retrieving directory entry for: " + key);
		synchronized (index) {
			return index.getEntry(key);
		}
	}

	//@Override
	public boolean removeEntry(String key) {
		log.info("Removing directory entry for: " + key);
		if (key == null)
			return false;

		synchronized (index) {
			index.remove(key);
		}
		log.debug(toString());
		return true;
	}

	//@Override
	public boolean sendHearbeat(String key, Object... objects) {
		log.info("Heartbeat recevived for key: " + key);
		synchronized (index) {
			Map<String, String> entry = this.getEntry(key);

			if (entry != null) {
				entry.put(LAST_UPDATED, String.valueOf(System.currentTimeMillis()));
				return true;
			} else
				return false;
		}
	}

	//@Override
	public boolean updateEntry(String key, Map<String, String> entry) {

		log.info("Updating directory entry for: " + key);

		if (key == null || entry == null)
			return false;

		synchronized (index) {
			entry.put(LAST_UPDATED, String.valueOf(System.currentTimeMillis()));

			// If key already exists, update entry
			Map<String, String> oldEntry = index.getEntry(key);

			if (oldEntry != null) {
				Iterator<String> i = entry.keySet().iterator();
				while (i.hasNext()) {
					String k = i.next();
					oldEntry.put(k, entry.get(k));
				}
			}
			// if not, use new entry
			else {
				index.updateIndex(key, entry);
			}
		}
		log.debug(toString());
		return true;
	}

	public String toString() {
		Calendar calendar = Calendar.getInstance();
		StringBuffer sb = new StringBuffer("\n"+calendar.getTime().toString() + ":\n");
		Set<String> set = index.keySet();
		for (String key : set) {
			Map<String, String> entry = index.getEntry(key);
			sb.append("[" + key + "," + entry.toString() + "] ");
		}
		return sb.toString();
	}

	public static class DirectoryServerWorker implements Runnable {

		public void run() {

			while (true) {
				try {

					int sec = Integer.parseInt(PRPLProperties.getProperty("prpl.index.checkpoint.period", String
							.valueOf(PRPLConstants.DEFAULT_INDEX_CHECKPOINT_PERIOD)));

					Thread.sleep(sec * 1000);
					// Clean up and persist
					synchronized (index) {
						// this.removeOldEntries();
						index.persist();
					}
					log.info("Directory entries were refreshed and persisted.");

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		private void removeOldEntries() {
			Set<String> set = index.keySet();
			Long currentTime = System.currentTimeMillis();
			for (String key : set) {
				Map<String, String> entry = index.getEntry(key);
				Long entryTime = Long.parseLong(entry.get(LAST_UPDATED));

				if (currentTime - entryTime > 60 * 000) {
					index.remove(key);
				}
			}
		}
	}

}
